package com.tally.server.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tally.server.domain.CommentExample;
import com.tally.server.domain.Post;
import com.tally.server.domain.PostExample;
import com.tally.server.dto.*;
import com.tally.server.enums.*;
import com.tally.server.mapper.PostMapper;
import com.tally.server.mapper.my.MyPostMapper;
import com.tally.server.utils.CopyUtil;
import com.tally.server.utils.DateUtil;
import com.tally.server.utils.UUIDUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;

@Service
public class PostService {
    private static final Logger logger = LoggerFactory.getLogger(PostService.class);

    @Autowired
    private PostMapper postMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private MyPostMapper myPostMapper;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    /**
     * 列表查询
     */
    public void list(PageDTO pageDTO, Integer userId) {
        PageHelper.startPage(pageDTO.getPage(), pageDTO.getSize());
        List<Post> postList = myPostMapper.selectShowPosts(userId);
        PageInfo<Post> pageInfo = new PageInfo<>(postList);
        pageDTO.setTotal(pageInfo.getTotal());
        List<PostDTO> postDTOList = CopyUtil.copyList(postList, PostDTO.class);
        for (PostDTO postDTO : postDTOList) {
            UserDTO userDTO = userService.getById(postDTO.getUserId());
//            List<CommentDTO> commentDTOList = commentService.getByPostId(postDTO.getId());
            postDTO.setAuthor(userDTO);
//            postDTO.setCommentList(commentDTOList);
        }
        pageDTO.setList(postDTOList);
    }

    /**
     * 保存，id有值时更新，无值时新增
     */
    public void save(PostDTO postDTO) {
        postDTO.setCommitType(CommitTypeEnum.PUBLISHED.getCode());
        Post post = CopyUtil.copy(postDTO, Post.class);
        if (StringUtils.isEmpty(postDTO.getId())) {
            this.insert(post);
        } else {
            this.update(post);
        }
    }

    /**
     * 新增
     */
    private void insert(Post post) {
        Date now = new Date();
        post.setId(UUIDUtil.getShortUUID());
        post.setCreateTime(now);
        post.setUpdateTime(now);
        post.setIsDeleted(DeletedStatusEnum.USED.getCode());
        post.setStatus(PostStatusEnum.INSERT.getCode());
        post.setLike(0);
        post.setComments(0);
        postMapper.insert(post);
    }

    /**
     * 更新
     */
    private void update(Post post) {
        post.setUpdateTime(new Date());
        post.setStatus(PostStatusEnum.UPDATE.getCode());
        postMapper.updateByPrimaryKeyWithBLOBs(post);
    }

    /**
     * 删除
     */
    public void delete(String id) {
        Post post = postMapper.selectByPrimaryKey(id);
        post.setIsDeleted(DeletedStatusEnum.DELETED.getCode());
        post.setComments(0);
        post.setLike(0);
        post.setUpdateTime(new Date());
        postMapper.updateByPrimaryKey(post);
    }

    public PostDTO getById(String id) {
        Post post = postMapper.selectByPrimaryKey(id);
        PostDTO postDTO = CopyUtil.copy(post, PostDTO.class);
        CommentExample commentExample = new CommentExample();
        commentExample.createCriteria().andPostIdEqualTo(id);
        // 把评论列表也查出来
        List<CommentDTO> commentDTOList = commentService.getByPostId(id);
        postDTO.setCommentList(commentDTOList);
        return postDTO;
    }

    public void addLikes(String id, Integer likeStatus) {
        Post post = postMapper.selectByPrimaryKey(id);
        Post postUpdate = new Post();
        postUpdate.setId(id);
        if (LikeStatusEnum.LIKE.getCode().equals(likeStatus)) {
            postUpdate.setLike(post.getLike() + 1);
        } else {
            postUpdate.setLike(post.getLike() - 1);
        }
        postMapper.updateByPrimaryKeySelective(postUpdate);
    }

    public void addComments(String postId) {
        Post post = postMapper.selectByPrimaryKey(postId);
        Post postUpdate = new Post();
        postUpdate.setId(postId);
        postUpdate.setComments(post.getComments() + 1);
        postMapper.updateByPrimaryKeySelective(postUpdate);
    }

    public Integer getComments(String id) {
        Post post = postMapper.selectByPrimaryKey(id);
        return post.getComments();
    }

    public void searchPost(SearchPostPageDTO pageDTO, Integer id) {
//        if (keyword == null)
        Integer year = pageDTO.getYear();
        Integer month = pageDTO.getMonth();
        String keyword = pageDTO.getKeyword();
        Date startDate = null, endDate = null;
        if (year != 0 && month != 0) {
            startDate = getStartDate(year, month);
            endDate = getEndDate(year, month);

        } else if (year != 0) {
            startDate = getStartDate(year, 1);
            endDate = getEndDate(year, 12);
        }
        PageHelper.startPage(pageDTO.getPage(), pageDTO.getSize());
        List<Post> postList = myPostMapper.searchPost(keyword, startDate, endDate, id);
        PageInfo<Post> pageInfo = new PageInfo<>(postList);
        pageDTO.setTotal(pageInfo.getTotal());
        List<PostDTO> postDTOList = CopyUtil.copyList(postList, PostDTO.class);
        for (PostDTO postDTO : postDTOList) {
            UserDTO userDTO = userService.getById(postDTO.getUserId());
            postDTO.setAuthor(userDTO);
        }
        pageDTO.setList(postDTOList);
    }

    public void searchPostWithEs(SearchPostPageDTO postPageDTO, Integer userId) throws IOException {
        String keyword;
        int page = postPageDTO.getPage();
        int size = postPageDTO.getSize();
        List<PostDTO> postDTOList = new ArrayList<>();
        int year = postPageDTO.getYear();
        int month = postPageDTO.getMonth();
        Date startDate = null, endDate = null;
        if (year != 0 && month != 0) {
            startDate = getStartDate(year, month);
            endDate = getEndDate(year, month);
        } else if (year != 0) {
            startDate = getStartDate(year, 1);
            endDate = getEndDate(year, 12);
        }

        Request request = new Request("GET", "/post/_search");
        // 构建请求
        JSONObject jsonRequestObj = new JSONObject();
        // 构建source
        jsonRequestObj.put("_source", "*");
        jsonRequestObj.put("from", (page - 1) * size);
        jsonRequestObj.put("size", size);
        jsonRequestObj.put("query", new JSONObject());
        jsonRequestObj.getJSONObject("query").put("bool", new JSONObject());
        jsonRequestObj.getJSONObject("query").getJSONObject("bool").put("must", new JSONArray());
        JSONArray mustArray = jsonRequestObj.getJSONObject("query").getJSONObject("bool").getJSONArray("must");

        // 有关键字
        if (!StringUtils.isEmpty(keyword = postPageDTO.getKeyword())) {

            // should
            JSONObject boolObject1 = new JSONObject();
            boolObject1.put("bool", new JSONObject());
            boolObject1.getJSONObject("bool").put("should", new JSONArray());
            // title
            JSONObject matchObj1 = new JSONObject();
            matchObj1.put("match", new JSONObject());
            matchObj1.getJSONObject("match").put("title", new JSONObject());
            matchObj1.getJSONObject("match").getJSONObject("title").put("query", keyword);
            matchObj1.getJSONObject("match").getJSONObject("title").put("boost", 2);

            // content
            JSONObject matchObj2 = new JSONObject();
            matchObj2.put("match", new JSONObject());
            matchObj2.getJSONObject("match").put("content", new JSONObject());
            matchObj2.getJSONObject("match").getJSONObject("content").put("query", keyword);
            matchObj2.getJSONObject("match").getJSONObject("content").put("boost", 1);

            // author
            JSONObject matchObj3 = new JSONObject();
            matchObj3.put("match", new JSONObject());
            matchObj3.getJSONObject("match").put("author", new JSONObject());
            matchObj3.getJSONObject("match").getJSONObject("author").put("query", keyword);
            matchObj3.getJSONObject("match").getJSONObject("author").put("boost", 0.1);

            boolObject1.getJSONObject("bool").getJSONArray("should").add(matchObj1);
            boolObject1.getJSONObject("bool").getJSONArray("should").add(matchObj2);
            boolObject1.getJSONObject("bool").getJSONArray("should").add(matchObj3);

            mustArray.add(boolObject1);

        } else {
            jsonRequestObj.put("sort", new JSONArray());
            JSONObject sortObject = new JSONObject();
            sortObject.put("update_time", new JSONObject());
            sortObject.getJSONObject("update_time").put("order", "desc");
            jsonRequestObj.getJSONArray("sort").add(sortObject);
        }

        // 有日期
        if (startDate != null && endDate != null) {
            JSONObject boolObject2 = new JSONObject();
            boolObject2.put("bool", new JSONObject());
            boolObject2.getJSONObject("bool").put("filter", new JSONObject());
            boolObject2.getJSONObject("bool").getJSONObject("filter").put("range", new JSONObject());
            boolObject2.getJSONObject("bool").getJSONObject("filter").getJSONObject("range").put("update_time", new JSONObject());
            boolObject2.getJSONObject("bool").getJSONObject("filter").getJSONObject("range").getJSONObject("update_time").put("gte", startDate);
            boolObject2.getJSONObject("bool").getJSONObject("filter").getJSONObject("range").getJSONObject("update_time").put("lte", endDate);

            mustArray.add(boolObject2);
        }

        // 通用，找出非privacy或者本人的文章
        JSONObject boolObject3 = new JSONObject();
        boolObject3.put("bool", new JSONObject());
        boolObject3.getJSONObject("bool").put("should", new JSONArray());

        JSONObject termObject1 = new JSONObject();
        termObject1.put("term", new JSONObject());
        termObject1.getJSONObject("term").put("privacy", 0);

        JSONObject termObject2 = new JSONObject();
        termObject2.put("term", new JSONObject());
        termObject2.getJSONObject("term").put("author_id", userId);

        boolObject3.getJSONObject("bool").getJSONArray("should").add(termObject1);
        boolObject3.getJSONObject("bool").getJSONArray("should").add(termObject2);

        mustArray.add(boolObject3);

        // 去掉删除的文章
        JSONObject boolObject4 = new JSONObject();
        boolObject4.put("bool", new JSONObject());
        boolObject4.getJSONObject("bool").put("should", new JSONArray());

        JSONObject termObject3 = new JSONObject();
        termObject3.put("term", new JSONObject());
        termObject3.getJSONObject("term").put("is_deleted", 0);
        boolObject4.getJSONObject("bool").getJSONArray("should").add(termObject3);
        mustArray.add(boolObject4);

        String reqJson = jsonRequestObj.toJSONString();
        logger.info(reqJson);
        request.setJsonEntity(reqJson);
        Response response = restHighLevelClient.getLowLevelClient().performRequest(request);
        String responseStr = EntityUtils.toString(response.getEntity());
        logger.info(responseStr);
        JSONObject jsonObject = JSONObject.parseObject(responseStr);
        Integer total = jsonObject.getJSONObject("hits").getJSONObject("total").getInteger("value");
        JSONArray jsonArray = jsonObject.getJSONObject("hits").getJSONArray("hits");

        for(int i=0; i<jsonArray.size(); i++) {
            PostDTO postDTO = new PostDTO();
            JSONObject jsonObj = jsonArray.getJSONObject(i).getJSONObject("_source");
            postDTO.setId(jsonObj.getString("id"));
            postDTO.setTitle(jsonObj.getString("title"));
            postDTO.setContent(jsonObj.getString("content"));
            postDTO.setCreateTime(jsonObj.getDate("create_time"));
            postDTO.setUpdateTime(jsonObj.getDate("update_time"));
            postDTO.setStatus(jsonObj.getInteger("status"));
            postDTO.setLike(jsonObj.getInteger("like"));
            postDTO.setComments(jsonObj.getInteger("comments"));
            postDTO.setPrivacy(jsonObj.getInteger("privacy"));
            postDTO.setUserId(jsonObj.getInteger("author_id"));
            UserDTO userDTO = userService.getById(postDTO.getUserId());
            postDTO.setAuthor(userDTO);
            postDTOList.add(postDTO);
        }

        postPageDTO.setTotal(total);
        postPageDTO.setList(postDTOList);
    }

    /**
     * 得到某年某月的第一天
     * @param yearI
     * @param monthI
     * @return
     */
    private Date getStartDate(Integer yearI,Integer monthI){
        LocalDate startLocalDate = LocalDate.of(yearI,monthI,1);
        return DateUtil.localDate2Date(startLocalDate);
    }

    /**
     * 得到某年某月的最后一天
     * @param yearI
     * @param monthI
     * @return
     */
    private Date getEndDate(Integer yearI,Integer monthI){
        LocalDate endLocalDate = LocalDate.of(yearI,monthI,DateUtil.lastDayOf(yearI,monthI));
        return DateUtil.localDate2Date(endLocalDate);
    }

    public PostDTO getByIdWithoutBlobs(String postId) {
        Post post = myPostMapper.selectByPrimaryKeyWithoutBLOBs(postId);
        return CopyUtil.copy(post, PostDTO.class);
    }
}
